﻿using System;
using System.Collections.Generic;
using System.IO;
using System.Net.Sockets;
using System.Threading;
using System.Xml;
using System.Runtime.Serialization.Formatters.Binary;
using ExamSharedClasses;
using ExamSharedClasses.Commands;



namespace ExamClient {

    /// <summary>
    /// (k) Die Erweiterung der Klasse Connection für den Client...
    /// Sie startet sofort den Thread, der auf ankommende Objekte hört und
    /// kann per Delegaten auch auf entsprechende Fenster mit angekommenen
    /// Objekten versorgen.
    /// </summary>
    public class ServerConnection : Connection {

        public delegate void del(); //-k saubere Verbindung zum STA-Thread möglich?

        public ServerConnection()
            : base() {
            Connect();
            Start();
        }
        public override void Connect() {
            try {
                base.Connect();
            } catch ( Exception ex ) {
                App.Display( "Fehler: Herstellen der Verbindung nicht möglich", ex );
            }
        }
        public override void ConnectionThreadMethod() {
            try {
                do { ProcessReceivedObject( Receive() ); }
                while ( Client != null );
            } catch ( Exception ex ) {
                Close();
                App.Display( "Fehler: Verbindung wurde abgebrochen!", ex );
            }

        }

        public void Ping() {
            Send( new Session( App.Session.SessionId, new Ping( App.SOURCE ) ) );
        }
        public void Login() {
            Send( new Session( new Login( App.UserAddress.UserEmail, App.Password ) ) );
        }
        public void Logout() {
            if ( IsLoggedIn ) {
                try {
                    Send( new Logout( App.SOURCE ) );
                }
                catch (Exception ex)
                { App.Display("Logout ServerConnection: " + ex); }
                
            }
            base.Close();
        }

        public override object Receive() {
            try {
                object o = base.Receive();
                App.Display( "EMPFANGEN: " + o.ToString() );
                return o;
            } catch ( Exception ex ) {
                App.Display( ex );
                //Connect();
                Close();
                return null;
            }
        }
        /// <summary>
        /// (k) Diese Methode zum Senden erweitert die Basismethode, die lediglich das Objekt
        /// serialisiert verschickt, um einige praktische Elemente:
        /// Sollte versucht werden, nur ein Commando-Objekt zu verschicken, dann wird es automatisch
        /// in eine Session eingebettet, deren enthaltene Informationen auch ggf. vervollständigt
        /// werden, SessionId und App.SOURCE.
        /// </summary>
        /// <param name="o"></param>
        public override void Send( object o ) {

            try {                                   // automatische Vorgänge:
                if ( o is Command ) {
                    Command c = ( Command )o;
                    o = new Session( c );
                }

                if ( o is Session ) {
                    Session s = ( Session )o;
                    s.SessionId = App.Session.SessionId;    //  -SessionId in Sessionobjekt eintragen
                    Command c = s.Command;
                    if ( c.Source.Equals( "" ) )        //  -Commando.Source-Property ausfüllen, wenn nicht schon geschehen...
                        c.Source = App.SOURCE;
                }
            } catch ( Exception ex ) {
                App.Display( ex );
            }

            try {
                App.Display( "SENDE: " + o.ToString() );
                base.Send( o );
            } catch ( Exception ex ) {
                Close();
                App.Display( ex );
            }
        }

        public void ProcessReceivedObject( object o ) {

            try {
                if ( o is Session ) {
                    Session receivedSession = ( Session )o;
                    Command receivedCommand = receivedSession.Command;
                    App.Display( "Sessionübereinstimmung: " + App.Session.Validate( receivedSession ) );

                    if ( receivedCommand is Message ) {
                        Message message = ( Message )receivedCommand;
                        if ( message.MessageId == 667 ) { // Loginerteilung, bedeutet auch, daß User-Objekt im Session-Objekt ist, oder?
                            App.Session = receivedSession;
                            App.UserAddress = ( UserAddress )receivedSession.User;
                            IsLoggedIn = true;
                            App.MainWindow.Dispatcher.BeginInvoke(
                                new del( () => {
                                    App.MainWindow.ChangePagesAfterLogin();
                                }
                            ) );
                        } else if (message.MessageId == 666) {
                            App.MainWindow.Dispatcher.BeginInvoke(
                                new del(() => {
                                    App.MainWindow.SetStatusBarMessage(message.MessageText);
                                }
                            ));
                        }


                    } else if ( ( receivedCommand is Ping ) && ( receivedCommand.Source != App.SOURCE ) ) {
                        Send( receivedCommand );

                    } else if ( receivedCommand is ViewExam ) {
                        App.MainWindow.Dispatcher.BeginInvoke(
                            new del( () => {
                                ViewExam ve = ( ViewExam )receivedCommand;
                                if ( ve.Exam is ExamUser || App.UserAddress.UserGroup.GroupId == 2 || App.UserAddress.UserGroup.GroupId == 4 ) {
                                    // dann ausgefüllte Klasur nur anzeigen
                                    if ( App.UserAddress.UserGroup.GroupId == 1 ) {
                                        App.MainWindow.center.Content = new ViewTextPage( ve.GetWrittenExamText() ); // Ausgefüllte Klausur
                                    } else {
                                        App.MainWindow.center.Content = new ViewTextPage( ( ( ExamWithQuestions )ve.Exam ).ToFullExamWithAnswersString() ); // Vorlage mit Antworten
                                    }
                                } else {
                                App.MainWindow.center.Content = new ExamPage( ve );
                                }
                            }) ); 

                    } else if ( receivedCommand is ViewClass ) {
                        App.MainWindow.Dispatcher.BeginInvoke(
                            new del( () => {
                                if ( App.MainPage != null )
                                    App.MainPage.AddReceivedUsers( ( ViewClass )receivedCommand );
                            }
                        ) );
                    } else if ( receivedCommand is ViewUsers ) {
                        App.MainWindow.Dispatcher.BeginInvoke(
                            new del( () => {
                                if ( App.MainPage != null )
                                    App.MainPage.AddReceivedUsers( ( ViewUsers )receivedCommand );
                            }
                        ) );

                    } else if ( receivedCommand is ViewExams ) {
                        App.MainWindow.Dispatcher.BeginInvoke(
                            new del( () => {
                                if ( App.MainPage != null ) {
                                    App.MainPage.AddReceivedExams( ( ViewExams )receivedCommand );
                                }
                            }
                        ) );


                    } else if ( receivedCommand is Logout ) {
                        Logout receivedLogout = ( Logout )receivedCommand;
                        App.Display( "LOGOUT: von " + receivedLogout.Source + " empfangen." );
                        if (receivedLogout.Source != App.SOURCE) {
                            Send(receivedLogout);
                        } else {
                            Close();
                        }
                        App.Display( "LOGOUT: ausfuehren." );

                    } else
                        App.Display( "Fehler: Code wird nicht unterstützt!" );
                }

            } catch ( Exception ex ) {
                App.Display( ex );
            }
        }

    }
}
